﻿using Ardalis.GuardClauses;
using HRX.PayCenter.ApplicationCore.Entities;
using HRX.PayCenter.ApplicationCore.Exceptions;
using HRX.PayCenter.ApplicationCore.Interfaces;
using HRX.PayCenter.ApplicationCore.Models;
using HRX.PayCenter.ApplicationCore.Specifications;
using HRX.PayCenter.Common;
using HRX.PayCenter.Contract;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using OSS.Common.ComModels;
using OSS.Common.Encrypt;
using OSS.Common.Extention;
using OSS.Http;
using OSS.PaySdk.Ali.Pay;
using OSS.PaySdk.Ali.Pay.Mos;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static HRX.PayCenter.ApplicationCore.Specifications.BizConst;

namespace HRX.PayCenter.ApplicationCore.Services
{
    /// <summary>
    /// alipay交易服务
    /// </summary>
    public class AlipayTradeService : IPayService
    {
        private readonly IAppLogger<AlipayTradeService> _appLogger;
        private readonly HttpManager _httpManager;
        private readonly ZPayTradeApi _zPayTradeApi;
        private readonly ZPayTransferApi _zPayTransferApi;
        private readonly ZPayRefundApi _zPayRefundApi;
        private readonly IAlipayOrderRepository _alipayOrderRepository;
        private readonly IRefundRepository _refundRepository;
        private readonly IDiviseRepository _diviseRepository;
        private readonly PayCenterConfig _payCenterConfig;
        private readonly IDeviceApiExternalService _deviceApiExternalService;

        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="appLogger"></param>
        /// <param name="httpManager"></param>
        /// <param name="AlipayOrderRepository"></param>
        /// <param name="refundRepository"></param>
        /// <param name="diviseRepository"></param>
        /// <param name="payCenterConfigWarp"></param>
        /// <param name="zPayTradeApi"></param>
        /// <param name="zPayTransferApi"></param>
        /// <param name="zPayRefundApi"></param>
        /// <param name="deviceApiExternalService"></param>
        public AlipayTradeService(IAppLogger<AlipayTradeService> appLogger, HttpManager httpManager,
            IAlipayOrderRepository AlipayOrderRepository,
            IRefundRepository refundRepository,
            IDiviseRepository diviseRepository,
            IOptions<PayCenterConfigWarpper> payCenterConfigWarp,
            ZPayTradeApi zPayTradeApi,
            ZPayTransferApi zPayTransferApi,
            ZPayRefundApi zPayRefundApi,
            IDeviceApiExternalService deviceApiExternalService)
        {
            _appLogger = appLogger;
            _httpManager = httpManager;

            _zPayTradeApi = zPayTradeApi;
            _zPayTransferApi = zPayTransferApi;
            _zPayRefundApi = zPayRefundApi;

            _alipayOrderRepository = AlipayOrderRepository;
            _refundRepository = refundRepository;
            _diviseRepository = diviseRepository;

            _payCenterConfig = payCenterConfigWarp.Value?.PayCenterConfig;

            _deviceApiExternalService = deviceApiExternalService;
        }

        /// <summary>
        /// 唤起wap支付并创建订单
        /// </summary>
        /// <param name="alipayTradeRequest"></param>
        /// <returns></returns>
        public async Task<string> GetWapTradeContentWithOrder(TradeRequest alipayTradeRequest)
        {
            //解密私密信息
            var ext = CommonSecurity.DecryptWithAES(alipayTradeRequest.Ext.Base64UrlDecode(), _payCenterConfig.SecurityKey);
            //解析
            var extObj = NewtonsoftJsonHelper.DeserializeObject<AlipayTradeExtModel>(ext);

            //计算分成
            var diviseAmount = BizHelper.GetDiviseAmount(alipayTradeRequest.Amount, extObj.DiviseRate);
            DateTime dtNow = DateTime.Now;
            //生成订单号
            var orderNo = IdGenHelper.GenOrderNo(OrderNoFlag.AlipayFlag, dtNow);

            DateTime defDateTIme = new DateTime(1900, 1, 1);
            var alipayOrderEntity = await _alipayOrderRepository.AddAsync(new AlipayOrderEntity
            {
                AddTime = dtNow,
                Amount = alipayTradeRequest.Amount,
                CarOwerId = extObj.CarOwerId,
                CarTel = extObj.CarTel,
                CompanyAlipayId = extObj.CompanyAlipayId,
                CompanyAmount = diviseAmount.companyAmount,
                CompanyId = extObj.CompanyId,
                DeviceId = alipayTradeRequest.DeviceId,
                DiviseRate = extObj.DiviseRate,
                HeadCompanyAlipayId = extObj.HeadCompanyAlipayId, //总公司一般不参与分账，剩余的应该就是总共公司的钱？
                HeadCompanyAmount = diviseAmount.headCompanyAmout,
                HeadCompanyId = extObj.HeadCompanyId,
                InvestorAlipayId = extObj.InvestorAlipayId,
                InvestorAmount = diviseAmount.investorAmount,
                InvestorId = extObj.InvestorId,
                OrderNo = orderNo,
                PayerAlipayId = "",
                Status = (int)StatusEnum.WaitPay,
                CloseTime = defDateTIme,
                DiviseTime = defDateTIme,
                LastModifyTime = defDateTIme,
                PayTime = defDateTIme,
                RefundTime = defDateTIme,
                DeviceCompleteTime = defDateTIme,
                DeviceStartTime = defDateTIme,
                ProductId = alipayTradeRequest.ProductId,
                Licenseplate = extObj.Licenseplate,
                PayTradeNo = "",
                AlipayTradeStatus = "",
                RefundFee = 0
            });

            if (alipayOrderEntity.Id > 0)
            {
                try
                {
                    ResultMo<string> result = _zPayTradeApi.GetWapTradeContent(new ZAddWapTradeReq
                    {
                        total_amount = alipayTradeRequest.Amount,
                        out_trade_no = orderNo,
                        subject = "支付设备费用",
                    });

                    return result.data;
                }
                catch
                {
                    return $"{orderNo}";
                }
            }

            return "订单创建失败，请重新提交";
        }

        /// <summary>
        /// 退款
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<bool> Refund(RefundRequest request)
        {
            //判断订单是否存在
            var orderEntity = _alipayOrderRepository.GetByWhere(a => a.OrderNo == request.OrderNo)?.FirstOrDefault();
            if (orderEntity == null)
            {
                Guard.Against.VaildException($"订单（{request.OrderNo}）不存在");
            }
            if (orderEntity.Status == (int)StatusEnum.Refunded)
            {
                Guard.Against.VaildException($"订单（{request.OrderNo}）已退款，不可重复退款");
            }
            if (orderEntity.Status != (int)StatusEnum.Success)
            {
                Guard.Against.VaildException($"订单（{request.OrderNo}）未支付成功，不可退款");
            }

            DateTime dtNow = DateTime.Now;
            string refundNo = IdGenHelper.GenOrderNo(OrderNoFlag.AlipayRefundFlag, dtNow);
            var response = await _zPayRefundApi.RefunPayAsync(new ZPayRefundReq
            {
                out_trade_no = orderEntity.OrderNo,
                trade_no = orderEntity.PayTradeNo,
                refund_amount = orderEntity.Amount,
                refund_reason = "正常退款",
                out_request_no = refundNo
            });

            if (!response.IsSuccess())
            {
                _appLogger.LogError(null, $"[支付宝退款失败] {orderEntity.OrderNo}|{orderEntity.PayTradeNo} 原因：{response.msg}");
                Guard.Against.VaildException($"订单（{request.OrderNo}）退款失败 {response.msg}");
            }

            //保存业务系统
            RefundEntity refundEntity = new RefundEntity
            {
                AddTime = dtNow,
                AlipayRefundTradeNo = response.trade_no,
                AlipayTradeNo = orderEntity.PayTradeNo,
                Amount = response.refund_fee,
                OrderNo = orderEntity.OrderNo,
                RefundNo = refundNo,
                RefundTime = response.gmt_refund_pay,
                Status = response.fund_change == "Y" ? 2 : 3,
                LastModifyTime = dtNow
            };

            refundEntity = await _refundRepository.AddAsync(refundEntity);
            if (refundEntity.Id > 0)
            {
                if (refundEntity.Status == 2)
                {
                    orderEntity.Status = 40;
                    orderEntity.RefundTime = refundEntity.RefundTime;
                    orderEntity.LastModifyTime = dtNow;
                    await _alipayOrderRepository.UpdateAsync(orderEntity);
                }

                return true;
            }

            return false;
        }

        /// <summary>
        /// 转账
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<bool> Transfer(DiviseRequet request)
        {
            DateTime dtNow = DateTime.Now;
            var orderEntity = _alipayOrderRepository.GetByWhere(a => a.OrderNo == request.OrderNo)?.FirstOrDefault();
            if (orderEntity == null)
            {
                Guard.Against.VaildException($"订单（{request.OrderNo}）不存在");
            }

            if (request.UserId == 0)
            {
                if (orderEntity.Status == (int)StatusEnum.Divised)
                {
                    Guard.Against.VaildException($"订单（{request.OrderNo}）已分账，不可重复分账");
                }
                if (orderEntity.Status != (int)StatusEnum.Success)
                {
                    Guard.Against.VaildException($"订单（{request.OrderNo}）未支付成功，不可以分账");
                }
            } //针对单独用户作为补充，如果不是则验证状态

            //获取已分账记录
            var listDivise = _diviseRepository.GetByWhere(d => d.OrderNo == orderEntity.OrderNo);
            if (listDivise.IsNullOrEmpty())
            {
                listDivise = new List<DiviseEntity>();
            }

            var diviseEntity1 = listDivise.FirstOrDefault(d => d.TargetType == 2 && d.Status == 2);
            if (diviseEntity1 == null && (request.UserId == 0 || (orderEntity.CompanyId == request.UserId && request.UserType == (int)UserType.Sub)))
            {
                var diviseEntity = await DiviseReturnEntityAsync(UserType.Sub, orderEntity.CompanyId, orderEntity.OrderNo,
                    orderEntity.PayTradeNo, orderEntity.CompanyAlipayId, orderEntity.CompanyAmount);

                await _diviseRepository.AddAsync(diviseEntity);

                if (diviseEntity1 != null)
                {
                    diviseEntity1.IsDeleted = true;
                    diviseEntity1.LastModifyTime = DateTime.Now;
                    await _diviseRepository.UpdateAsync(diviseEntity1);
                }
            }  //子公司
            var diviseEntity2 = listDivise.FirstOrDefault(d => d.TargetType == 3 && d.Status == 2);
            if (diviseEntity2 == null && (request.UserId == 0 || (orderEntity.InvestorId == request.UserId && request.UserType == (int)UserType.Investor)))
            {
                var diviseEntity = await DiviseReturnEntityAsync(UserType.Investor, orderEntity.InvestorId, orderEntity.OrderNo,
                    orderEntity.PayTradeNo, orderEntity.InvestorAlipayId, orderEntity.InvestorAmount);

                await _diviseRepository.AddAsync(diviseEntity);

                if(diviseEntity2!=null)
                {
                    diviseEntity2.IsDeleted = true;
                    diviseEntity2.LastModifyTime = DateTime.Now;
                    await _diviseRepository.UpdateAsync(diviseEntity2);    
                }
            } //投资人

            orderEntity.Status = (int)StatusEnum.Divised;
            orderEntity.LastModifyTime = dtNow;
            orderEntity.DiviseTime = dtNow;
            await _alipayOrderRepository.UpdateAsync(orderEntity);

            return true;
        }

        /// <summary>
        /// 子公司分账
        /// </summary>
        /// <returns></returns>
        private async Task<DiviseEntity> DiviseReturnEntityAsync(
            UserType userType, int userId,
            string orderNo, string alipayTradeNo,
            string alipayId, decimal amount)
        {
            DateTime dtNow = DateTime.Now;
            DiviseEntity diviseEntity = new DiviseEntity
            {
                AddTime = dtNow,
                AlipayId = alipayId,
                Amount = amount,
                OrderNo = orderNo,
                TargetType = (int)userType,
                TargetUserId = userId,
                AlipayTradeNo = alipayTradeNo,
                Status = 3,
                TransferNo = IdGenHelper.GenOrderNo(OrderNoFlag.AlipayTranFlag, dtNow),
                DiviseTime = new DateTime(1900, 1, 1),
                LastModifyTime = new DateTime(1900, 1, 1)
            };

            if (string.IsNullOrEmpty(diviseEntity.AlipayId)
                //|| !diviseEntity.AlipayId.StartsWith("2088")
                || diviseEntity.Amount <= 0)
            {
                return diviseEntity;
            }

            ZPayTransferResp resp;
            try
            {
                resp = await _zPayTransferApi.TransferAsync(new ZPayTransferReq
                {
                    amount = diviseEntity.Amount.ToString("f2"),
                    out_biz_no = diviseEntity.TransferNo,
                    payee_type = "ALIPAY_LOGONID", //ALIPAY_USERID
                    payee_account = diviseEntity.AlipayId
                });

                if (!resp.IsSuccess())
                {
                    _appLogger.LogError(null,
                        $"[支付宝转账失败]订单号{diviseEntity.OrderNo},支付宝交易号{diviseEntity.AlipayTradeNo},转账编号{diviseEntity.AlipayTradeNo} 错误信息{resp.code}-{resp.msg};{resp.sub_code}-{resp.sub_msg}");
                    return diviseEntity;
                }
            }
            catch (Exception)
            {
                resp = new ZPayTransferResp();
                resp.ret = 0;
                resp.pay_date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                //throw;
            }

            diviseEntity.AlipayTransferOrderId = resp.order_id;
            if (string.IsNullOrEmpty(resp.pay_date))
            {
                return diviseEntity;
            }

            diviseEntity.Status = 2;
            diviseEntity.DiviseTime = resp.pay_date.ToDateTime();
            return diviseEntity;
        }

        /// <summary>
        /// 验证回调签名
        /// </summary>
        /// <param name="formDics"></param>
        /// <returns></returns>
        public bool CheckCallBackSign(IDictionary<string, string> formDics)
        {
            var checkResult = _zPayTradeApi.CheckCallBackSign(formDics);
            if (!checkResult.IsSuccess())
            {
                _appLogger.LogError(null, 
                    $"[支付宝回调验签失败] {formDics.ToStringFromDict()}");
                return false;
            } //验证签名
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="zPayCallBack"></param>
        /// <param name="isAsync">是否异步更新</param>
        /// <returns></returns>
        public async Task<bool> CheckOrderOrUpdate(ZPayCallBackResp zPayCallBack, bool isAsync)
        {
            /* 实际验证过程建议商户添加以下校验。
             1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
             2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
             3、校验通知中的seller_id（或者seller_email) 是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email）
             4、验证app_id是否为该商户本身。
             */
            var orderEntity = _alipayOrderRepository.
                GetByWhere(a => a.OrderNo == zPayCallBack.out_trade_no).FirstOrDefault();
            if (orderEntity == null)
            {
                _appLogger.LogError(null, $"[支付宝回调] 订单号{zPayCallBack.out_trade_no}不存在 => {NewtonsoftJsonHelper.SerializeObject(zPayCallBack)}");
                return false;
            }  //判断订单状态
            if (orderEntity.Amount != zPayCallBack.total_amount)
            {
                _appLogger.LogError(null, 
                    $"[支付宝回调] 订单号{zPayCallBack.out_trade_no}金额不一致 => {NewtonsoftJsonHelper.SerializeObject(zPayCallBack)}");
                return false;
            }
            if (zPayCallBack.seller_id != _zPayTradeApi.ZPayConfig.PID)
            {
                _appLogger.LogError(null, 
                    $"[支付宝回调] 订单号{zPayCallBack.out_trade_no} 合作伙伴身份（PID）{_zPayTradeApi.ZPayConfig.PID}不一致 => {NewtonsoftJsonHelper.SerializeObject(zPayCallBack)}");
                return false;
            }
            if (zPayCallBack.app_id != _zPayTradeApi.ZPayConfig.AppId)
            {
                _appLogger.LogError(null, 
                    $"[支付宝回调] 订单号{zPayCallBack.out_trade_no} 应用Id {_zPayTradeApi.ZPayConfig.AppId}不一致 => {NewtonsoftJsonHelper.SerializeObject(zPayCallBack)}");
                return false;
            }
            if (!isAsync)
            {
                return true;
            }
            //交易状态
            //判断该笔订单是否在商户网站中已经做过处理
            //如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
            //请务必判断请求时的total_amount与通知时获取的total_fee为一致的
            //如果有做过处理，不执行商户的业务程序

            //注意：
            //退款日期超过可退款期限后（如三个月可退款），支付宝系统发送该交易状态通知

            //更新状态
            if (zPayCallBack.trade_status == orderEntity.AlipayTradeStatus)
            {
                _appLogger.LogInformation(null,
                    $"[支付宝回调-重复通知] 订单号{zPayCallBack.out_trade_no} 应用Id {_zPayTradeApi.ZPayConfig.AppId}不一致 => {NewtonsoftJsonHelper.SerializeObject(zPayCallBack)}");
                return true;
            } //如果状态已经存在库中 则直接返回

            bool isPaySuccess = false;
            if (zPayCallBack.trade_status == AlipayTradeStatus.TradeSuccess)
            {
                orderEntity.Status = (int)StatusEnum.Success;
                orderEntity.PayerAlipayId = zPayCallBack.buyer_logon_id;
                orderEntity.PayTime = zPayCallBack.gmt_payment.ToDateTime();
                isPaySuccess = true;
            }
            else if (zPayCallBack.trade_status == AlipayTradeStatus.TradeClosed)
            {
                orderEntity.Status = (int)StatusEnum.Closed;
                orderEntity.CloseTime = zPayCallBack.gmt_close.ToDateTime();
            }
            else if (zPayCallBack.trade_status == AlipayTradeStatus.TradeFinished)
            {
                if (orderEntity.Status == (int)StatusEnum.Success)
                {
                    orderEntity.Status = (int)StatusEnum.Finished;
                }
                else if (orderEntity.Status == (int)StatusEnum.Divised)
                {
                    orderEntity.Status = (int)StatusEnum.DivisedAndFinished;
                }
                else if (orderEntity.Status == (int)StatusEnum.Refunded)
                {
                    orderEntity.Status = (int)StatusEnum.RefundedAndFinished;
                }
            } //交易完成比较特殊
            else if (zPayCallBack.trade_status == AlipayTradeStatus.WaitBuyerPay)
            {
                orderEntity.Status = (int)StatusEnum.WaitPay;
            }

            orderEntity.AlipayTradeStatus = zPayCallBack.trade_status;
            orderEntity.PayTradeNo = zPayCallBack.trade_no;
            orderEntity.LastModifyTime = DateTime.Now;

            //更新数据
            await _alipayOrderRepository.UpdateAsync(orderEntity);
            if (isPaySuccess)
            {
                //通过进程内 通讯 解耦出去
                _deviceApiExternalService.StartDevice(orderEntity.OrderNo);
            }
            return true;
        }
    }
}
